#include "parser.hpp"

size_t finParenthese (string expression)
{
    size_t posParOuvrante = expression.find('(', 1);
    size_t posParFermante = expression.find(')');

    while ((posParFermante > posParOuvrante) && (posParOuvrante != string::npos))
    {
        posParOuvrante = expression.find('(', posParOuvrante+1);
        posParFermante = expression.find(')', posParFermante+1);
    }
    return posParFermante;
}

string enleveChar (string expression, const char element)
{
    size_t position;
    do
    {
        position = expression.find(element);
        if(position != string::npos)
            expression.erase(position, 1);
    } while((position != string::npos) && !expression.empty());
    return expression;
}

string enleveParentheseAutour (string expression, bool verbose)
{
    size_t positionDebutParenthese = expression.find("(");
    if(positionDebutParenthese != string::npos)
    {
        size_t positionFinParenthese = finParenthese(expression.substr(positionDebutParenthese));
        if(positionDebutParenthese == 0 && positionFinParenthese == expression.size()-1)
        {
            expression.erase(positionFinParenthese);
            expression.erase(positionDebutParenthese, 1);
        }
    }
    return expression;
}

bool verifieExpressionPolonaise(string expression, bool verbose)
{
    if(verbose)
        cout << "entrée dans verifieExpressionPolonaise, expr = " << expression << endl;

    if(expression.empty())
    {
        cout << "chaine incorrecte : la chaine est vide (peut être du à un manque d'opérateur à gauche)" << endl;
        return false;
    }
    else if (expression.find_first_not_of(charAccept) != string::npos)
    {
        cout << "chaine incorrecte : la chaine " << expression << " contient un ou des caractères non autorisé" << endl;
        cout << "les charactères autorisés sonts : " << charAccept << endl;
        return false;
    } else {
        size_t positionOperateur = expression.find_first_of(charOp);

        // il y a 3 cas possible avec le premier operateur :
        // - soit il est au debut de la chaine
        // - soit il est à la deuxième place, dans ce cas il y a une parenthèse avant
        // - soit il n'y en a pas et l'expression est juste une valeur
        if ((positionOperateur > 1) && (positionOperateur != string::npos))
        {
            cout << "chaine incorrecte" << endl;
            cout << "une chaine doit être de la forme op(expr)(expr)...(expr)" << endl;
            return false;
        }


        int nbParentheses = 0;
        bool parenthese = false; // teste si il y a au moins une parenthèse
        bool parentheseOuvrante = false; // teste si le dernier charactère était une parenthèse ouvrante
        bool operateur = false; // indique si le dernièr charactère était un opérateur
        bool moins = false; // indique si le dernièr chatactère était un moins
        char c;

        for(unsigned int i = 0; i<expression.size(); i++)
        {
            c = expression.at(i);
            if(parentheseOuvrante)
            {
                if (c == '(' || c == ')')
                {
                    cout << "chaine incorrecte : " << expression << " à la position " << i-1 << endl;
                    cout << "une parenthèse ouvrante doit être suivie d'un opérateur ou d'une valeur" << endl;
                    return false;
                }
                parentheseOuvrante = false;
            }
            if(operateur)
            {
                if(c != '(')
                {
                    cout << "chaine incorrecte : " << expression << " à la position " << i-1 << endl;
                    cout << "une chaine doit être de la forme op(expr)(expr)...(expr)" << endl;
                    return false;
                }
                if(i>=2)
                {
                    if(expression.at(i-2) != '(')
                    {
                        cout << "chaine incorrecte : " << expression << " à la position " << i-1 << endl;
                        cout << "une chaine doit être de la forme op(expr)(expr)...(expr)" << endl;
                        return false;
                    }
                }
                operateur = false;
            }

            if(moins)
            {
                if (c == '(' || c == ')' || c == '+' || c == '*' || c == '-')
                {
                    cout << "chaine incorrecte : " << expression << " à la position " << i << endl;
                    cout << "un opérateur - ne peut être placé que devant un chiffre" << endl;
                    return false;
                }
                moins = false;
            } else {
                if(c == '(')
                {
                    parenthese = true;
                    parentheseOuvrante = true;
                    nbParentheses ++;
                }
                else if(c == ')')
                    nbParentheses --;
                else if(c == '+' || c == '*')
                    operateur = true;
                else if(c == '-')
                    moins = true;
                //dans le dernier cas c'est un chiffre
            }

            if(nbParentheses < 0)
            {
                cout << "chaine incorrecte : il y a une parenthèse fermante avant une parenthése ouvrante à la position " << i << endl;
                return false;
            }
        }

        if(nbParentheses > 0)
        {
            cout << "chaine incorrecte : il y a trop de parenthèses ouvrantes" << endl;
            return false;
        }

        if(operateur || moins)
        {
            cout << "chaine incorrecte : probablement incomplète" << endl;
            return false;
        }

        if(c != ')' && parenthese)
        {
            cout << "chaine incorrecte : une chaine doit finir par une parenthèse a moins d'être une simple valeur" << endl;
            return false;
        }
        return true;
    }
}

bool verifieExpression(string expression, bool verbose)
{
    if(expression.empty())
    {
        cout << "chaine incorrecte : la chaine est vide (peut être du a un manque d'opérateur à gauche)" << endl;
        return false;
    }
    else if (expression.find_first_not_of(charAccept) != string::npos)
    {
        cout << "chaine incorrecte : la sous chaine " << expression << " contient un caractère non autorisé" << endl;
        cout << "les charactères autorisés sonts : " << charAccept << endl;
        return false;
    }
    else
    {
        size_t positionNonChiffre = expression.find_first_not_of(charNum);
        if(positionNonChiffre == string::npos) // si il n'y a rien d'autre que des chiffres qui se suivent
        {
            return true;
        }

        size_t positionDebutParenthese = expression.find_first_of("(");
        size_t positionFinParenthese = expression.find_first_of(")");
        if(positionFinParenthese < positionDebutParenthese) // si il y a une parenthèse fermante avant une parenthése ouvrante
        {
            cout << "chaine incorrecte : il y a une parenthèse fermante avant une parenthése ouvrante dans la sous chaine " << expression << endl;
            return false;
        }

        size_t positionOperateur = expression.find_first_of(charOp);

        while (positionDebutParenthese != string::npos)
        {
            positionFinParenthese = finParenthese(expression.substr(positionDebutParenthese)) + positionDebutParenthese;
            if (positionFinParenthese == string::npos) // si il manque une parenthèse fermante
            {
                cout << "chaine incorrecte : il manque une parenthèse fermante dans la sous chaine " << expression << endl;
                return false;
            }

            if((positionOperateur != string::npos) && (positionOperateur<positionDebutParenthese))
            {// si il y a un opérateur avant les parenthéses
                size_t positionChiffre = expression.find_first_of(charNum, positionOperateur);
                if((positionChiffre - positionOperateur) == 1 || (positionDebutParenthese - positionOperateur) == 1)
                { // si le prochain élement est un chiffre ou une parenthése ouvrante
                    bool retour = (verifieExpression(expression.substr(0, positionOperateur), verbose) &&
                            verifieExpression(expression.substr(positionOperateur +1), verbose));
                    return retour;
                }
                cout << "chaine incorrecte : l'operateur " << expression.at(positionOperateur);
                cout << " n'as pas d'élément valide à droite dans la sous chaine " << expression << endl;
                return false;
            }

            if(! verifieExpression(expression.substr(positionDebutParenthese+1, (positionFinParenthese - positionDebutParenthese) - 1), verbose))
                return false;

            positionDebutParenthese = expression.find("(", positionFinParenthese);
            positionOperateur = expression.find_first_of(charOp, positionFinParenthese);
        }

        // il n'y a pas ou plus de parenthéses dans la chaine
        while (positionOperateur != string::npos)
        {
            size_t positionChiffre = expression.find_first_of(charNum, positionOperateur);
            if(!((positionChiffre - positionOperateur) == 1))
            {
                cout << "chaine incorrecte : l'operateur " << expression.at(positionOperateur);
                cout << " n'as pas d'élément valide à droite dans la sous chaine " << expression << endl;
                return false;
            }

            positionOperateur = expression.find_first_of(charOp, positionOperateur+1);
        }
        return true;
    }
}

string ajouteMult(string expression, bool verbose)
{
    if(!expression.empty())
    {
        size_t positionDebutParenthese = expression.find("(");

        if(positionDebutParenthese == 0) // si c'est la première parenthése
        {
            positionDebutParenthese = expression.find("(", 1);
        }

        while (positionDebutParenthese != string::npos)
        {
            size_t positionChiffre = expression.find_first_of(charNum, positionDebutParenthese - 1);
            char c;
            c = expression.at(positionDebutParenthese-1);

            if((c == ')') || ((positionDebutParenthese - positionChiffre) == 1)) // si le caractère avant '(' est un ')'
            {
                expression.insert(positionDebutParenthese, "*");
                positionDebutParenthese ++;
            }
            positionDebutParenthese = expression.find("(", positionDebutParenthese+1);
        }
    }
    return expression;
}

string ajoutParenthese(string expression, bool verbose)
{
    if(!expression.empty())
    {
        size_t positionChiffre = expression.find_first_of(charNum);
        while (positionChiffre != string::npos)
        {
            size_t positionNonChiffre = expression.find_first_not_of(charNum, positionChiffre);
            expression.insert(positionChiffre, "(");
            if (positionNonChiffre == string::npos)
            {
                expression.append(")");
                positionChiffre = positionNonChiffre;
            }
            else
            {
                expression.insert(positionNonChiffre +1, ")");
                positionChiffre = expression.find_first_of(charNum, positionNonChiffre+2);
            }
        }
    }
    return expression;
}

string transformeExpression(const string expression, bool verbose)
{
    if (verbose)
        cout << "entrée dans transforme expression avec expression = " << expression << endl;

    string expr = expression;
    //si l'expression est entourée de deux parenthéses on les supprime
    size_t positionDebutParenthese = expr.find("(");
    if(positionDebutParenthese != string::npos)
    {
        size_t positionFinParenthese = finParenthese(expr.substr(positionDebutParenthese));
        if(positionDebutParenthese == 0 && positionFinParenthese == expr.size()-1)
        {
            expr.erase(positionFinParenthese);
            expr.erase(positionDebutParenthese, 1);
        }
    }

    size_t positionNonChiffre = expr.find_first_not_of(charNum);

    if(positionNonChiffre == string::npos) // si il n'y a que des chiffres
    {
        if (verbose)
            cout << "l'expression est une valeur" << endl;

        expr.insert(0, "(");
        expr.append(")");
        //exit(0);
        return expr;
    }
    else
    { //si c'est une opération (aka avec des opérateurs)
        if (verbose)
            cout << "l'expression est une opération" << endl;

        string temp; // temp servira a contenir les chaines renvoyées par la fonction récursive

        size_t positionDebutParenthese = expr.find("(");
        size_t positionFinParenthese = finParenthese(expr.substr(positionDebutParenthese))+positionDebutParenthese;
        size_t positionPlus = expr.find_first_of("+", positionFinParenthese);
        size_t positionMult = expr.find_first_of("*", positionFinParenthese);
        size_t positionDernierPlus = string::npos;
        size_t positionDernierMult = string::npos;
        size_t positionOperateur = expr.find_first_of(charOp, positionFinParenthese);

        if (positionPlus != string::npos)
        {
            if(verbose)
                cout << "l'opération contient des additions" << endl;
            while ((positionDebutParenthese != string::npos) && (positionPlus != string::npos)) // tant qu'il reste des + en dehors de parenthèses
            {
                if(verbose)
                {
                    cout << "position debut parenthèse : " << positionDebutParenthese << endl;
                    cout << "position fin parenthèse   : " << positionFinParenthese << endl;
                    cout << "position de l'operateur + : " << positionPlus << endl;

                }

                if(positionPlus != string::npos)
                {
                    cout << "\taddition : récursivité avec la chaine " << expr.substr(positionDernierPlus+1, (positionPlus - positionDernierPlus -1)) << endl;
                    temp.append(transformeExpression(expr.substr(positionDernierPlus+1, (positionPlus - positionDernierPlus -1)), verbose));
                    positionDernierPlus = positionPlus;
                }

                do {
                positionDebutParenthese = expr.find("(", positionFinParenthese);
                positionFinParenthese = finParenthese(expr.substr(positionDebutParenthese))+positionDebutParenthese;
                positionOperateur = expr.find_first_of(charOp, positionFinParenthese);
                } while (expr.at(positionOperateur) != '+' || positionOperateur == string::npos);
            }


            if (verbose)
                cout << "\tajout de la dernière addition" << endl;
            //gestion de la derniere expression a additionner
            temp.append(transformeExpression(expr.substr(positionDernierPlus +1), verbose));

            temp.insert(0, "(+");
            temp.append(")");

            if (verbose)
                cout << "etat de temp aprés ajout des additions " << temp << endl;

         //   positionDebutParenthese = expr.find("(");
           // positionFinParenthese = finParenthese(expr.substr(positionDebutParenthese))+positionDebutParenthese;
        }
        else  if (positionMult != string::npos)
        {
            if (verbose)
                cout << "l'opération contient des multiplications" << endl;
            int i = 0;
            while ((positionDebutParenthese != string::npos) && (positionMult != string::npos)) // tant qu'il reste des * en dehors de parenthèses
            {
                if(positionMult != string::npos)
                {
                    cout << "\tmultiplication : récursivité avec la chaine " << expr.substr(positionDernierMult+1, (positionMult - positionDernierMult -1)) << endl;
                    temp.append(transformeExpression(expr.substr(positionDernierMult+1, (positionMult - positionDernierMult -1)), verbose));
                    positionDernierMult = positionMult;
                }
                positionDebutParenthese = expr.find("(", positionMult);
                positionFinParenthese = finParenthese(expr.substr(positionDebutParenthese))+positionDebutParenthese;
                positionMult = expr.find_first_of("*", positionFinParenthese);
                i++;
                if(i > 3)
                    exit(0);
            }


            if(verbose)
                cout << "\tajout de la dernière multiplication" << endl;
                //gestion de la derniere expression a multiplier
            temp.append(transformeExpression(expr.substr(positionDernierMult +1), verbose));

            temp.insert(0, "(*");
            temp.append(")");

            if (verbose)
                cout << "etat de temp aprés ajout des multiplications " << temp << endl;
        }

        if(verbose)
            cout << endl << "retour de la chaine : " << temp << endl << endl;

        return temp;

    }
}
